package com.luo.d3s.core.application.assmebler;

import com.luo.d3s.core.domain.model.ValueObjectEnum;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.util.Objects;

/**
 * 基础转换器
 *
 * @author luohq
 * @date 2023-07-04 8:25
 */
public class BaseAssembler {

    private BaseAssembler() {
    }

    /**
     * 转换源对象为目标对象（支持ValueObjectEnum.getValue()属性拷贝）
     *
     * @param source      源对象
     * @param targetClass 目标对象类型
     * @param <T>         源对象类型
     * @param <R>         目标对象类型
     * @return 目标对象
     */
    public static <T, R> R convert(T source, Class<R> targetClass) {
        R target = null;
        //新建目标对象（无参构造函数）
        try {
            target = targetClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        //拷贝属性（同名称、同类型）
        BeanUtils.copyProperties(source, target);

        //特殊处理枚举类型拷贝
        copyVoEnumValue(source, target);

        return target;
    }

    /**
     * 拷贝ValueObjectEnum属性值<br/>
     * target.propVal = ((ValueObjectEnum) source.propVal).getValue()
     *
     * @param source 源对象
     * @param target 目标对象
     * @param <T>
     * @param <R>
     * @return 目标对象
     */
    public static <T, R> R copyVoEnumValue(T source, R target) {
        //特殊处理枚举类型拷贝
        BeanWrapperImpl sourceWrapper = new BeanWrapperImpl(source);
        BeanWrapperImpl targetWrapper = new BeanWrapperImpl(target);
        PropertyDescriptor[] sourcePropertyDescriptors = sourceWrapper.getPropertyDescriptors();
        PropertyDescriptor[] targetPropertyDescriptors = targetWrapper.getPropertyDescriptors();
        for (PropertyDescriptor sourcePropDesc : sourcePropertyDescriptors) {
            //判断源属性为ValueObjectEnum类型，且在目标属性集合中有同名不同类型的属性
            PropertyDescriptor targetPropDesc = matchVoEnumTypeAndPropName(sourcePropDesc, targetPropertyDescriptors);
            if (Objects.isNull(targetPropDesc)) {
                continue;
            }
            //设置目标属性值target.propVal = ((ValueObjectEnum) source.propVal).getValue()
            String sourcePropName = sourcePropDesc.getName();
            ValueObjectEnum sourcePropVoEnumVal = (ValueObjectEnum) sourceWrapper.getPropertyValue(sourcePropName);
            if (Objects.isNull(sourcePropVoEnumVal)) {
                continue;
            }
            targetWrapper.setPropertyValue(sourcePropName, sourcePropVoEnumVal.getValue());
        }
        return target;
    }

    /**
     * 判断源属性为ValueObjectEnum类型，且在目标属性集合中有同名不同类型的属性
     *
     * @param sourcePropDesc  源属性描述
     * @param targetPropDescs 目标属性描述集合
     * @return 匹配的目标属性描述
     */
    private static PropertyDescriptor matchVoEnumTypeAndPropName(PropertyDescriptor sourcePropDesc, PropertyDescriptor[] targetPropDescs) {
        //源属性为ValueObjectEnum
        Boolean sourcePropIsVoEnum = ValueObjectEnum.class.isAssignableFrom(sourcePropDesc.getPropertyType());
        if (!sourcePropIsVoEnum) {
            return null;
        }
        String sourcePropName = sourcePropDesc.getName();
        for (PropertyDescriptor targetPropDesc : targetPropDescs) {
            //参数名称相同且类型不同
            if (targetPropDesc.getName().equals(sourcePropName) && !sourcePropDesc.getPropertyType().equals(targetPropDesc.getPropertyType())) {
                return targetPropDesc;
            }
        }
        return null;
    }
}
